package ga.gene;

import java.util.ArrayList;
import java.util.Random;

import ga.gates.*;

/**
 * The Class Gene.
 */
public class Gene {
	
	private Input input1,input2;
	private Gates gate;
	private Input output = null;
	private static Random random = new Random();
	
	/**
	 * Instantiates a new gene.
	 *
	 * @param in1 the in1
	 * @param in2 the in2
	 * @param gateType the gate type
	 * @throws TheException the the exception
	 */
	public Gene(Input in1, Input in2, int gateType) throws TheException{
		input1 = in1;
		input2 = in2;


		gate = createGate(gateType);

	}
	
	/**
	 * Instantiates a new gene by simply picking a random input and random gate.
	 *
	 * @param inputs the inputs
	 * @throws TheException the the exception
	 */
	public Gene(ArrayList<Input> inputs) throws TheException{
		while(input1 == null || input2 == null){
			input1 = inputs.get(random.nextInt(inputs.size()));
			input2 = inputs.get(random.nextInt(inputs.size()));
		}
		gate = createGate(random.nextInt(7)+1);

	}
	
	/**
	 * A simple switch that will randomly generate a gate
	 *
	 * @param input the input
	 * @return the gates
	 * @throws TheException the the exception
	 */
	public Gates createGate(int input) throws TheException{
		switch(input){
		case 0:
			return new Wire();
		case 1:
			return new NotGate();
		case 2:
			return new AndGate();
		case 3:
			return new OrGate();
		case 4:
			return new NandGate();
		case 5:
			return new NorGate();
		case 6:
			return new XorGate();
		case 7:
			return new XnorGate();
		default:
			throw new TheException("Invalid gate intered");
		}
	}
	
	/**
	 * Generates the output and stores it to the Gene
	 *
	 * @return true, if successful
	 * @throws TheException the the exception
	 */
	public boolean geneOutput() throws TheException{
		try{
			output.setBool(gate.eval(input1.getBool(), input2.getBool()));
			return output.getBool();
		}
		catch(NullPointerException e){
			throw new TheException("Can't compare null values");			
		}
	}
	
	/**
	 * Gets the input1.
	 *
	 * @return the input1
	 */
	public Input getInput1() {
		return input1;
	}
	
	/**
	 * Gets the input2.
	 *
	 * @return the input2
	 */
	public Input getInput2() {
		return input2;
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	public String toString(){
		if(gate.toString().equalsIgnoreCase("NOT"))
			return input1 + " " + gate + " = " + output;
		return input1 + " " + gate + " " + input2 + " = " + output;
	}
	
	/**
	 * To bool.
	 *
	 * @return the string
	 */
	public String toBool(){
		if(gate.toString().equalsIgnoreCase("NOT"))
			return input1.getBool() + " " + gate + " = " + output.getBool();
		return input1.getBool() + " " + gate + " " + input2.getBool() + " = " + output.getBool();
	}
	
	/**
	 * Sets the output.
	 *
	 * @param output the new output
	 */
	public void setOutput(Input output) {
		this.output = output;
	}
	
	/**
	 * Gets the output.
	 *
	 * @return the output
	 */
	public Input getOutput() {
		return output;
	}
	
	/**
	 * Mutate.
	 *
	 * @throws TheException the the exception
	 */
	public void mutate() throws TheException {
		gate = createGate(random.nextInt(7)+1);
		
	}
}
